#include <sstream>
#include <logger.hpp>
#include <server/frame/ProgramVersion.h>
#include "Command.h"

__CmdStats * CCommandStats::stats_ = 0;

template<class T>
static void deleteCmd(T * p)
{
    Tools::Destroy(p, std::allocator<T>());
}

//struct QCmdBase
std::string QCmdBase::CommandName(int cmdtype)
{
    switch(cmdtype){
        //ADD NEW COMMAND HERE
        case CMD_QUERY:
            return "QUERY_CMD";
        case CMD_RESP:
            return "RESP_CMD";
        case CMD_INSERT:
            return "INSERT_CMD";
        default:{
            std::ostringstream oss;
            oss<<"INVALID_ID_"<<cmdtype;
            return oss.str();
        }
    }
}

//just for convenience use
#define __CREATE_COMMAND(CMD_CLASS) \
    ret = std::allocator<CMD_CLASS>().allocate(1);  \
    LOG(DEBUG, "create command "<<#CMD_CLASS<<"="<<ret);   \
    new (ret) CMD_CLASS

#define __DELETE_COMMAND(CMD_CLASS) \
    LOG(DEBUG, "release command "<<#CMD_CLASS<<"="<<pCmd<<pCmd->ToString());    \
    deleteCmd(dynamic_cast<CMD_CLASS *>(pCmd))

QCmdBase * QCmdBase::CreateCommand(const __Buf & data,size_t * used)
{
    LOG(DEBUG,"QCmdBase::CreateCommand");
    CInDataStream ds(data);
    ds.SeekFromBegin(CMD_TYPE_OFFSET);
    U32 type = 0;
    if(!(ds>>type))
        return 0;
    QCmdBase * ret = 0;
    switch(type){
        //ADD NEW COMMAND HERE
        case CMD_QUERY:{
            __CREATE_COMMAND(CQueryCmd);
            break;}
        case CMD_INSERT:{
            __CREATE_COMMAND(CInsertCmd);
            break;}
        default:{
            LOG(ERROR, "create unknown command type="<<U32(type));
        }
    }
    if(ret){
        ds.SeekFromBegin(0);
        if(ret->Decode(ds)){
            if(used)
                *used = ds.CurPos();
        }else{
            LOG(ERROR, "decode cmd error, release it");
            ReleaseCommand(ret);
        }
    }
    return ret;
}

void QCmdBase::ReleaseCommand(QCmdBase *& pCmd)
{
    LOG(DEBUG, "QCmdBase::ReleaseCommand");
    if(!pCmd)
        return;
    int cmdtype = pCmd->CmdType();
    switch(cmdtype){
        //ADD NEW COMMAND HERE
        case CMD_QUERY:{
            __DELETE_COMMAND(CQueryCmd);
            break;}
        case CMD_INSERT:{
            __DELETE_COMMAND(CInsertCmd);
            break;}
        default:
            LOG(ERROR, "release unknown command type="<<cmdtype
                <<" address="<<pCmd);
    }
    pCmd = 0;
}

#undef __CREATE_COMMAND
#undef __DELETE_COMMAND

//struct CQueryCmd
std::string CQueryCmd::ToStringHelp() const
{
    std::ostringstream oss;
    oss<<"(cid_="<<Tools::DumpHex(cid_)
        <<",offset_="<<offset_
        <<",amount_="<<amount_
        <<")";
    return oss.str();
}

bool CQueryCmd::DecodeParam(CInDataStream & ds)
{
    LOG(DEBUG, "CQueryCmd::DecodeParam");
    ds>>cid_>>offset_>>amount_;
    LOG(DEBUG, ToString());
    return ds && cid_.length() == 20;
}

//struct CRespCmd
CRespCmd::CRespCmd(const QCmdBase & head)
    : RCmdBase(CMD_RESP,head)
    , errCode_(0)
{}

std::string CRespCmd::ToStringHelp() const
{
    std::ostringstream oss;
    oss<<"(errCode_="<<errCode_;
    for(size_t i = 0;i < results_.size();++i)
        oss<<","<<results_[i].ToString();
    oss<<")";
    return oss.str();
}

void CRespCmd::EncodeParam(COutDataStream & ds) const
{
    LOG(DEBUG, "CRespCmd::EncodeParam");
    LOG(DEBUG, ToString());
    ds<<errCode_;
    if(!errCode_){
        ds<<U32(results_.size());
        for(size_t i = 0;i < results_.size();++i)
            ds<<results_[i];
    }
}

//struct CInsertCmd
std::string CInsertCmd::ToStringHelp() const
{
    std::ostringstream oss;
    oss<<"(cid_="<<Tools::DumpHex(cid_)
        <<",gcid_="<<Tools::DumpHex(gcid_)
        <<",fileSz_="<<fileSz_;
    size_t i = 0;
    for(;i < 100 && i < peerId_.size();++i)
        oss<<",peerId_["<<i<<"]="<<peerId_[i];
    if(i < peerId_.size())
        oss<<",...";
    oss<<")";
    return oss.str();
}

bool CInsertCmd::DecodeParam(CInDataStream & ds)
{
    LOG(DEBUG, "CQueryCmd::DecodeParam");
    U32 sz;
    if(!(ds>>cid_>>gcid_>>fileSz_>>sz))
        return false;
    peerId_.resize(sz);
    for(size_t i = 0;i < sz;++i)
        if(!(ds>>peerId_[i]))
            return false;
    LOG(DEBUG, ToString());
    return ds;
}

